function hfig = view_frames(fs, siz)
%VIEW_FRAMES View a sequence of frames in a window with single-axis
%
%   VIEW_FRAMES(fs);
%
%       Create a GUI window and show frames on it.
%
%       Here, fs can be either of the following:
%       - a numeric array of size h x w x n
%       - a cell array of images
%       - an object of class frameseq
%       - {genfun, indices}, a pair composed of an image-generating
%         function handle and a vector of indices.
%
%           genfun(i) yields an image for each i in indices
%
%   VIEW_FRAMES(fs, siz);
%
%       Create a GUI with a single-axis of specified size and show
%       frames on it.
%
%       All images to be shown will be first resized to siz.
%
%       If siz is not given, it will be inferred from the first frame
%       produced by fs.
%

% Created by Dahua Lin, on Mar 22, 2012
%

%% verify input arguments

if nargin < 2
    siz = [];
else
    if ~(isnumeric(siz) && isequal(size(siz), [1 2]))
        error('view_frames:invalidarg', ...
            'siz should be a pair of numbers.');
    end
end
   
if isnumeric(fs)
    n = size(fs, 3);
    f = @(i) fs(:,:,i);
    
elseif iscell(fs) && isnumeric(fs{1})
    n = numel(fs);
    f = @(i) fs{i};        
    
elseif isa(fs, 'frameseq')
    n = fs.nframes;
    f = @(i) fs.frame(i);
    
elseif iscell(fs) && numel(fs) == 2 && isa(fs{1}, 'function_handle')
    genfun = fs{1};
    indices = fs{2};
    n = numel(indices);
    f = @(i) genfun(indices(i));
    
else
    error('view_frames:invalidarg', ...
        'The first argument fs is not valid.');
end

%% main

% decide size

if isempty(siz)
    F = f(1);
    h = size(F, 1);
    w = size(F, 2);
else
    h = siz(1);
    w = siz(2);
end

% create GUI

handles = create_gui(h, w, n);
hfig = handles.fig;
curindex = 1;
set_frame_index(1);

set(hfig, 'Visible', 'on');
movegui(hfig, 'center');

%% Event Handlers

    function slider_callback(~, ~)
        
        i = get(handles.slider, 'Value');
        i = round(i);
        
        if i ~= curindex
            set_frame_index(i);                
            set(handles.tindex, 'String', num2str(i));
        end
    end


    function tindex_callback(~, ~)
        
        isz = get(handles.tindex, 'String');
        i = str2double(isz);
        
        if isnan(i) || ~(i == fix(i) && i >= 1 && i <= n)
            set(handles.tindex, 'String', num2str(curindex));
        else            
            if i ~= curindex
                set_frame_index(i);
                set(handles.slider, 'Value', i);
            end
        end        
    end


    function set_frame_index(i)
        
        axes(handles.axis); %#ok<MAXES>
        img = f(i);
        curindex = i;
        
        if ~(size(img, 1) == h && size(img, 2) == w)
            img = imresize(img, [h w]);
        end
        
        imshow(img);        
    end


%% GUI layout

    function handles = create_gui(h, w, n)
        
        % layout config
        
        mg_l = 25;
        mg_r = 25;
        mg_t = 20;
        mg_b = 25;
        y_sep = 15;
        sld_h = 20;
        tindex_h = 20;
        tindex_w = 60;
        
        fig_h = mg_t + mg_b + h + y_sep * 2 + sld_h + tindex_h;
        fig_w = mg_l + mg_r + w;
        
        % generate figure
        
        hfig = figure;
        set(hfig, ...
            'Units', 'pixels', 'Position', [0 0 fig_w, fig_h], ...
            'Resize', 'off', 'MenuBar', 'none');
        
        % generate main axis
        
        ax_x = mg_l;
        ax_y = mg_b + sld_h + y_sep;
        
        hax = axes('Units', 'pixels',  ...
            'Position', [ax_x, ax_y, w, h], ...
            'YDir', 'reverse', 'XLim', [0 w], 'YLim', [0 h], ...
            'XTick', [], 'YTick', [], ...
            'NextPlot', 'replacechildren');
        
        % generate slider
        
        sld_x = mg_l;
        sld_y = mg_b;
        
        minor_step = 1 / (n - 1);
        major_step = 0.05;
        
        hsld = uicontrol(hfig, 'Style', 'slider', ...
            'Position', [sld_x, sld_y, w, sld_h], ...
            'Min', 1, 'Max', n, 'Value', 1, ...
            'SliderStep', [minor_step, major_step], ...
            'Callback', @slider_callback);
        
        % generate tindex (index textbox)
        
        tindex_x = mg_l;
        tindex_y = ax_y + h + y_sep;
        
        htindex = uicontrol(hfig, 'Style', 'edit', ...
            'Position', [tindex_x, tindex_y, tindex_w, tindex_h], ...
            'String', '1', ...
            'Callback', @tindex_callback);
        
        
        % all handles
        
        handles.fig = hfig;
        handles.axis = hax;
        handles.slider = hsld;
        handles.tindex = htindex;
        
    end
    

end





